package com.net.web.server.server;

import com.net.common.entity.CacheBean;
import com.net.common.entity.HttpCarrier;
import com.net.common.entity.Message;
import com.net.common.util.CloseableUtils;
import com.net.common.util.DateUtils;
import com.net.common.util.MessageSerializableUtils;
import com.net.web.server.config.ServerConfig;
import com.net.web.server.http.AnalysisHttpRequest;
import com.net.web.server.spring.SpringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Date;
import java.util.Objects;

import static com.net.web.server.config.ServerMapContext.cacheMap;
import static com.net.web.server.config.ServerMapContext.socketWanMap;
import static com.net.common.constant.Constant.HttpMethod.GET;
import static com.net.common.constant.Constant.MessageType.FORWARD;

/**
 * 接收浏览器消息
 *
 * @author hjh
 */
@Slf4j
public class SocketWanRunnable implements Runnable {
    /**
     * 浏览器socket
     */
    private final Socket socketWan;
    /**
     * 客户端输出流
     */
    private final OutputStream clientOut;
    /**
     * 浏览器输入流
     */
    private final InputStream inWan;

    /**
     * 浏览器输入流
     */
    private final OutputStream outWan;
    /**
     * 客户端socket标识
     */
    private final Long sessionId;
    /**
     * 浏览器socket标识
     */
    private final Long socketWanId;


    @SneakyThrows
    public SocketWanRunnable(Socket socketWan, Socket clientSocket, Long sessionId, Long socketWanId) {
        this.socketWan = socketWan;
        this.clientOut = clientSocket.getOutputStream();
        this.inWan = socketWan.getInputStream();
        this.outWan = socketWan.getOutputStream();
        this.sessionId = sessionId;
        this.socketWanId = socketWanId;
    }

    @Override
    public void run() {
        while (!socketWan.isClosed() && socketWan.isConnected()) {
            try {
                // 解析http请求
                AnalysisHttpRequest request = new AnalysisHttpRequest(inWan);
                if (request.getData() == null || request.getData().length == 0) {
                    break;
                }

                ServerConfig serverConfig = SpringUtils.getBean(ServerConfig.class);
                // 查询是否命中缓存
                if (Objects.nonNull(serverConfig) && serverConfig.isEnableCache() && GET.equals(request.getMethod()) && cacheMap.containsKey(sessionId) && cacheMap.get(sessionId).containsKey(request.getUri() + request.getHttpSessionId())) {
                    cacheWrite(request);
                    break;
                }

                // 这是锁的是一个客户端
                synchronized (clientOut) {
                    HttpCarrier carrier = new HttpCarrier(request.getMethod(), request.getUri(), request.getHttpSessionId(), request.getUri() + request.getHttpSessionId(), System.currentTimeMillis());
                    // 发送消息给客户端
                    MessageSerializableUtils.sendSerializableMessage(clientOut, () -> new Message(FORWARD, request.getData(), carrier, sessionId, socketWanId));
                }
            } catch (Exception e) {
                break;
            }
        }
        // 释放并删除连接
        CloseableUtils.close(inWan, outWan, socketWan);
        if (Objects.nonNull(socketWanMap.get(sessionId))) {
            socketWanMap.get(sessionId).remove(socketWanId);
        }
    }

    /**
     * 输出缓存数据
     *
     * @param request
     * @throws IOException
     */
    private void cacheWrite(AnalysisHttpRequest request) throws IOException {
        String cacheKey = request.getUri() + request.getHttpSessionId();
        CacheBean cacheBean = cacheMap.get(sessionId).get(cacheKey);

        outWan.write(cacheBean.getCache());
        // 惰性删除缓存
        if (cacheBean.getExpirationTime().before(new Date())) {
            cacheMap.get(sessionId).remove(cacheKey);
        } else {
            // 缓存续期
            cacheBean.setExpirationTime(DateUtils.addTime(CacheBean.CACHE_TIME));
        }
        log.info("{}->命中缓存", request.getUri());
    }
}
